<?php
/** The default autoloader
 *
 *
 */
class Yz_Autoloader
{

    /** Initialize autoloader
     *  @param string the path to libs directory, usually ./lib
     *  @return int 0 on success
     */
    public static function init($libDir)
    {
        static $initDone;
        if (! $initDone) {
            defined('YZ_LIB') or define('YZ_LIB', realpath($libDir) . '/');

            set_include_path( YZ_LIB );

            class_alias(get_called_class(), 'Autoloader');
            spl_autoload_register( 'Autoloader::autoload', true );
            $initDone = true;
        }
        return 0;
    }


    /**
     *  Load a class if it had not been loaded before
     *  Called from spl_autoload(), do not call directly
    *   @param string $className
    *   @return string $className
    *   @throw ClassError
    */
    public static function autoload( $className )
    {
        $loaded = static::doLoadClass($className);
        if (false === $loaded) {
            // avoiding possible infinite recursion
            require_once( YZ_LIB . 'Yz/Error/ClassError.php' );
            throw ClassError::_new_(array(
                'className' => $className,
            ));
        }
        return $loaded;
    }


    /**
     *  User-API method to load a class or verify that it's loaded
    *   @param string className
    *   @return className|false
    */
    public static function loadClass( $className )
    {
        assert('is_string($className)');

        if ( class_exists( $className, false ) or interface_exists( $className, false ) )
            return $className;
        return static::doLoadClass($className);
    }


    /**
     *  Deny instantiation
     */
    public function __construct()
    {
        throw NotImplementedError::_new_();
    }


    /**
     *  Deny static calls except the explicitly defined methods
     */
    public static function __callStatic($method, $args)
    {
        throw MethodError::_new_(array('method' => $method));
    }



    /**
     *  Load a class for the Yz system, replacing any namespace separator or underscore
     *      by directory separator and adding .php:
     *      Yz\modules\Users\Model_User -> Yz/modules/Users/Model/User.php
     *      NOTE: files are looked for in YZ_LIB directory, except the following case:
     *      NOTE: if no separators encountered, the class is assumed to be an Error class
     *          and looked for in YZ_LIB/Yz/Error directory
     *  @param string className
     *  @return className|false
     */
    protected static function doLoadClass( $class )
    {
        $classPath = strtr( $class, '_\\', '//' );
        if ($classPath === $class)  //using comparison is faster than a strpos fn call
            $classPath = 'Yz/Error/' . $class;
        $fileName = YZ_LIB . $classPath . '.php'; // use YZ_LIB to be sure that no chdir() leads to failure
        if ( is_file($fileName) ) {
            include_once( $fileName );
            // finally, check if the file does contain the class
            if ( class_exists( $class, false ) or interface_exists( $class, false ) )
                return $class;
            // avoiding possible infinite recursion
            require_once( YZ_LIB . 'Yz/Error/ClassError.php' );
            throw ClassError::_new_(array( 'className' => $class ));
        }
        return false;
    }

}
